class SIMPLE_NOTEPAD_8

insert
	IUP_INTERFACE

create {ANY}
   make

feature {ANY}

	ml: IUP_MULTILINE

	cfg: IUP_CONFIG

	str_find (str, str_to_find, casesensitive: STRING; pos: INTEGER): INTEGER
		do
			if str.count.is_equal(0) or str_to_find.count.is_equal(0) then
				Result := -1
			elseif casesensitive.is_equal("OFF") then
				str.to_lower
				str_to_find.to_lower
			end

			Result := str.substring_index(str_to_find, pos)
		end

	read_file (file_name: STRING): STRING
		local
			tfr: TEXT_FILE_READ
			str: STRING
			ms: IUP_MESSAGE
		do
			create str.make_empty
         create tfr.connect_to(file_name)

			if tfr.is_connected then
				from
					tfr.read_line
				until
					tfr.end_of_input
				loop
					str.append(tfr.last_string)
					str.append_character('%N')
					tfr.read_line
				end
				
				str.append(tfr.last_string)
				tfr.disconnect
			else
				str.append("Fail when reading from file: ")
				str.append(file_name)
				create ms.message("Error", str)
			end
			
			Result := str
		end

	write_file (file_name, text: STRING): INTEGER
		local
			tfw: TEXT_FILE_WRITE
			ms: IUP_MESSAGE
			str: STRING
		do
			create tfw.connect_to(file_name)

			if tfw.is_connected then
				tfw.put_string(text)
				tfw.disconnect
				Result := 1
			else
				str.make_empty
				str.append("Fail when writing to file: ")
				str.append(file_name)
				create ms.message("Error", str)
				Result := 0
			end
		end

--********************************** Callbacks *****************************************

	edit_menu_open_cb (m: IUP_MENU): STRING
		local
			cb: IUP_CLIPBOARD
			item_p, item_c, item_cp, item_del: IUP_MENU_ITEM 
		do
			cb := get_clipboard
			item_p ::= m.get_dialog_child("ITEM_PASTE")
			item_c ::= m.get_dialog_child("ITEM_CUT")
			item_cp ::= m.get_dialog_child("ITEM_COPY")
			item_del ::= m.get_dialog_child("ITEM_DELETE")

			if cb.is_text_available then
				item_p.set_active(True)
			else
		      item_p.set_active(False)
			end

			if ml.get_selected_text.is_empty then
				item_c.set_active(False)
				item_cp.set_active(False)
				item_del.set_active(False)
			else
				item_c.set_active(True)
				item_cp.set_active(True)
				item_del.set_active(True)
			end
			
			Result := "IUP_DEFAULT"
		end
	
	item_recent_cb (c: IUP_CONFIG): STRING
		local
			text: STRING
		do
			text := read_file(c.get_title)
			ml.set_value(text)

			Result := "IUP_DEFAULT"
		end
	
	config_recent_cb (c: IUP_CONFIG): STRING
		local
			file, text: STRING
		do
			file := c.get_title
			text := read_file(file)
			ml.set_value(text)
			
			Result := "IUP_DEFAULT"
		end

	multitext_caret_cb (m: IUP_TEXT; lin, col, pos: INTEGER): STRING
		local
			lbl_statusbar: IUP_LABEL
			str: STRING
		do
			if lbl_statusbar ?:= m.get_dialog_child("STATUSBAR") then
				lbl_statusbar ::= m.get_dialog_child("STATUSBAR")
				str := "Line "
				str.append(lin.to_string)
				str.append(", Col ")
				str.append(col.to_string)
				lbl_statusbar.set_title(str)
			end
			
			Result := "IUP_DEFAULT"
		end
	
	item_open_action_cb (widget: IUP_WIDGET): STRING
		local
			status: INTEGER
			rv, name, text: STRING
			fd: IUP_FILE_DIALOG
		do
			create fd.file_dialog
			fd.set_dialog_type("OPEN")
			fd.set_ext_filter("Text files|*.txt|All Files|*.*|")
			fd.set_title("Select a text file")

			rv := fd.popup_predefined_xy("IUP_CENTER", "IUP_CENTER")

			if rv.is_equal("IUP_NOERROR") then
				status := fd.get_status

				if status.is_equal(0) then
					name := fd.get_value
					text := read_file(name)
					cfg.update_recent_file_list(name)
					ml.set_value(text)
				end
			end

			fd.destroy
			Result := "IUP_DEFAULT"
		end

	item_saveas_action_cb (widget: IUP_WIDGET): STRING
		local
			status, r: INTEGER
			rv, name, text: STRING
			fd: IUP_FILE_DIALOG
		do
			create fd.file_dialog
			fd.set_dialog_type("SAVE")
			fd.set_ext_filter("Text files|*.txt|All Files|*.*|")
			fd.set_title("Select a text file")

			rv := fd.popup_predefined_xy("IUP_CENTER", "IUP_CENTER")

			if rv.is_equal("IUP_NOERROR") then
				status := fd.get_status

				if not status.is_equal(-1) then
					name := fd.get_value
					text := ml.get_value
					
					r := write_file(name, text)
					if r.is_equal(1) then
						cfg.update_recent_file_list(name)
					end
				end
			end

			fd.destroy
			Result := "IUP_DEFAULT"
		end

	item_font_action_cb (widget: IUP_MENU_ITEM): STRING
		local
			status: INTEGER
			rv, ft, selected: STRING
			fd: IUP_FONT_DIALOG
		do
			create fd.font_dialog
			ft := ml.get_font
			fd.set_value(ft)

			rv := fd.popup_predefined_xy("IUP_CENTER", "IUP_CENTER")

			if rv.is_equal("IUP_NOERROR") then
				status := fd.get_status

				if status.is_equal(1) then
					selected := fd.get_value
					ml.set_font(selected)
					cfg.set_variable_string("MainWindow", "Font", selected)
				end
			end

			fd.destroy
			Result := "IUP_DEFAULT"
		end

	item_about_action_cb (widget: IUP_MENU_ITEM): STRING
		local
			ms: IUP_MESSAGE
		do
			create ms.message("About","    Simple Notepad%N%NAutors:%N   Gustavo Lyrio%N   Antonio Scuri")
			Result := "IUP_DEFAULT"
		end

	item_exit_action_cb (widget: IUP_MENU_ITEM): STRING
		local
			i: INTEGER
			d: IUP_DIALOG
		do
			d := widget.get_dialog
			cfg.save_dialog (d, "MainWindow")
			i := cfg.save
			cfg.destroy
			
			Result := "IUP_CLOSE"
		end

	close_dialog_cb (d: IUP_DIALOG): STRING
		local
			i: INTEGER
		do
			cfg.save_dialog (d, "MainWindow")
			i := cfg.save
			cfg.destroy
			
			Result := "IUP_CLOSE"
		end

	goto_ok_action_cb(widget: IUP_BUTTON): STRING
		local
			line_count, line: INTEGER
			txt: IUP_TEXT
			ms: IUP_MESSAGE
		do
			if txt ?:= widget.get_dialog_child("LINE_TEXT") then
				txt ::= widget.get_dialog_child("LINE_TEXT")

				line_count := widget.get_attribute_integer("TEXT_LINECOUNT")
				line := txt.get_value.to_integer

				if line < 1 and line >= line_count then
					create ms.message("Error", "Invalid line number.")
					Result := "IUP_DEFAULT"
				end

				widget.get_dialog.set_attribute_integer("STATUS", 1)
				Result := "IUP_CLOSE"
			end
		end

	goto_cancel_action_cb(widget: IUP_BUTTON): STRING
		do
			widget.get_dialog.set_attribute_integer("STATUS", 0)

			Result := "IUP_CLOSE"
		end

	item_goto_action_cb (widget: IUP_WIDGET): STRING
		local
			dlg, d: IUP_DIALOG
			vbox: IUP_VBOX
			hbox: IUP_HBOX
			bt_ok, bt_cancel: IUP_BUTTON
			f: IUP_FILL
			lbl: IUP_LABEL
			txt: IUP_TEXT
			str, s: STRING
			line_count, line, pos: INTEGER
			item: IUP_MENU_ITEM
		do
			line_count := ml.get_line_count

			str := "Line Number [1-"
			str.append_string(ml.get_line_count.to_string)
			str.append_string("]:")

			create lbl.label(str)
			create txt.text
			txt.set_mask("IUP_MASK_UINT") -- unsigned integer numbers only
			txt.set_name("LINE_TEXT")
			txt.set_visible_columns(20)
			create bt_ok.button("OK")
			bt_ok.set_attribute_integer("TEXT_LINECOUNT", line_count)
			bt_ok.set_padding(10, 2)
			bt_ok.set_cb_action(agent goto_ok_action_cb(?))
			create bt_cancel.button("Cancel")
			bt_cancel.set_padding(10, 2)
			bt_cancel.set_cb_action(agent goto_cancel_action_cb(?))
			create f.fill

			create hbox.hbox({ARRAY[IUP_WIDGET] 1, << f, bt_ok, bt_cancel >>})
			hbox.set_normalize_size("HORIZONTAL")
			create vbox.vbox({ARRAY[IUP_WIDGET] 1, << lbl, txt, hbox >>})
			vbox.set_margin(10, 10)
			vbox.set_gap(5)

			create dlg.dialog(vbox)
			dlg.set_title("Go To Line")
			dlg.set_modal_dialog_frame(True)
			dlg.set_default_enter_widget(bt_ok)
			dlg.set_default_escape_widget(bt_cancel)

			if {IUP_MENU_ITEM} ?:= widget then
				item ::= widget
				dlg.set_parent_dialog_widget(item.get_dialog)
			elseif {IUP_DIALOG} ?:= widget then
				d ::= widget
				dlg.set_parent_dialog_widget(d)
			end

			s := dlg.popup_predefined_xy ("IUP_CENTERPARENT", "IUP_CENTERPARENT")

			if s.is_equal("IUP_NOERROR") and dlg.get_attribute_integer("STATUS").is_equal(1) then
				line := txt.get_value.to_integer
				pos := ml.text_convert_lin_col_to_pos(line, 0)
				ml.set_multiline_caret_pos(pos)
				ml.scroll_to_pos(pos)
			end

			dlg.destroy
			
			Result := "IUP_DEFAULT"
		end

	find_next_action_cb (widget: IUP_BUTTON): STRING
		local
			str, str_to_find, casesensitive: STRING
			find_pos, pos, end_pos: INTEGER
			txt: IUP_TEXT
			find_case: IUP_TOGGLE
			tup: TUPLE[INTEGER, INTEGER]
			pw: IUP_WIDGET
			ms: IUP_MESSAGE
		do
			str := ml.get_value
			find_pos := ml.get_attribute_integer("FIND_POS")

			if find_pos = 0 then
				find_pos := 1
			end

			if txt ?:= widget.get_dialog_child("FIND_TEXT") then
				txt ::= widget.get_dialog_child("FIND_TEXT")

				str_to_find := txt.get_value

				if find_case ?:= widget.get_dialog_child("FIND_CASE") then
					find_case ::= widget.get_dialog_child("FIND_CASE")

					casesensitive := find_case.get_value

					-- Rest 1 since the string start at 1, but multiline position
					-- start at 0.
					pos := str_find(str, str_to_find, casesensitive, find_pos) - 1

					if pos < 0 and find_pos > 0 then
						-- try again from the start
						pos := str_find(str, str_to_find, casesensitive, 1) - 1
					end

					if pos > 0 then
						end_pos := pos + str_to_find.count
						ml.set_attribute_integer("FIND_POS", end_pos)
						pw := ml.set_focus
						ml.set_multiline_selection_pos(pos, end_pos)

						tup := ml.text_convert_pos_to_lin_col(pos)
						-- position at col=0, just scroll lines
						pos := ml.text_convert_lin_col_to_pos(tup.item_1, 0)
						ml.scroll_to_pos(pos)
					else
						create ms.message("Warning", "Text not found.")
					end
				end
				
			end
			
			Result := "IUP_DEFAULT"
		end

	find_close_action_cb (widget: IUP_BUTTON): STRING
		do
			widget.get_dialog.hide

			Result := "IUP_DEFAULT"
		end

	item_find_action_cb (widget: IUP_WIDGET): STRING
		local
			dlg, main_dlg: IUP_DIALOG
			lbl: IUP_LABEL
			txt: IUP_TEXT
			btn, bt_next, bt_close: IUP_BUTTON
			find_case: IUP_TOGGLE
			f: IUP_FILL
			hbox: IUP_HBOX
			vbox: IUP_VBOX
			s: STRING
			item: IUP_MENU_ITEM
		do
			if btn ?:= widget then
				btn ::= widget
				main_dlg := btn.get_dialog
			elseif item ?:= widget then
				item ::= widget
				main_dlg := item.get_dialog
			elseif main_dlg ?:= widget then
				main_dlg ::= widget
			end

			if dlg ?:= main_dlg.get_attribute_widget("FIND_DIALOG") then
				dlg ::= main_dlg.get_attribute_widget("FIND_DIALOG")
			end

			if dlg = Void then
				create lbl.label("Find What:")
				create txt.text
				txt.set_name("FIND_TEXT")
				txt.set_visible_columns(20)
				create find_case.toggle("Case Sensitive")
				find_case.set_name("FIND_CASE")
				create bt_next.button("Find Next")
				bt_next.set_padding(10, 2)
				bt_next.set_cb_action(agent find_next_action_cb(?))
				create bt_close.button("Close")
				bt_close.set_padding(10, 2)
				bt_close.set_cb_action(agent find_close_action_cb(?))
				create f.fill

				create hbox.hbox({ARRAY[IUP_WIDGET] 1, << f, bt_next, bt_close >>})
				hbox.set_normalize_size("HORIZONTAL")
				create vbox.vbox({ARRAY[IUP_WIDGET] 1, << lbl, txt, find_case, hbox >>})
				vbox.set_margin(10, 10)
				vbox.set_gap(10)

				create dlg.dialog(vbox)
				dlg.set_title("Find")
				dlg.set_modal_dialog_frame(True)
				dlg.set_default_enter_widget(bt_next)
				dlg.set_default_escape_widget(bt_close)
				dlg.set_parent_dialog_widget(main_dlg)

				-- Save the dialog to reuse it
				main_dlg.set_attribute_widget("FIND_DIALOG", dlg)
			end
			
			-- center parent first time, next time reuse the last position
			s := dlg.show_predefined_xy("IUP_CURRENT", "IUP_CURRENT")

			Result := "IUP_DEFAULT"
		end

	item_copy_action_cb(item: IUP_MENU_ITEM): STRING
		local
			cp: IUP_CLIPBOARD
		do
			cp := get_clipboard
			cp.add_text(ml.get_selected_text)

			Result := "IUP_DEFAULT"
		end

	item_paste_action_cb(item: IUP_MENU_ITEM): STRING
		local
			cp: IUP_CLIPBOARD
		do
			cp := get_clipboard
			ml.insert(cp.get_text)
			
			Result := "IUP_DEFAULT"
		end

	item_cut_action_cb(item: IUP_MENU_ITEM): STRING
		local
			cp: IUP_CLIPBOARD
		do
			cp := get_clipboard
			cp.add_text(ml.get_selected_text)
			ml.set_selected_text("")

			Result := "IUP_DEFAULT"
		end

	item_delete_action_cb(item: IUP_MENU_ITEM): STRING
		do
			ml.set_selected_text("")
			
			Result := "IUP_DEFAULT"
		end

	item_select_all_action_cb(item: IUP_MENU_ITEM): STRING
		do
			ml.select_all

			Result := "IUP_DEFAULT"
		end

	key_pressed (dlg: IUP_DIALOG; k: INTEGER): STRING
		do
			if k.is_equal(iup_open.x_key_ctrl('O')) then
				Result := item_open_action_cb(dlg)
			elseif k.is_equal(iup_open.x_key_ctrl('S')) then
				Result := item_saveas_action_cb(dlg)
			elseif k.is_equal(iup_open.x_key_ctrl('F')) then
				Result := item_find_action_cb(dlg)
			elseif k.is_equal(iup_open.x_key_ctrl('G')) then
				Result := item_goto_action_cb(dlg)
			else
				Result := "IUP_DEFAULT"
			end
		end

	--********************************** Make *****************************************

	make
      local
			i: STRING
			x: INTEGER
			gui: IUP
			v: IUP_VBOX
			w: IUP_DIALOG
			item_exit, item_open, item_saveas, item_font, item_about: IUP_MENU_ITEM
			item_find, item_goto, item_copy, item_paste, item_cut, item_delete, item_select_all: IUP_MENU_ITEM
			sep1, sep2, sep3: IUP_MENU_SEPARATOR
			file_menu, edit_menu, format_menu, help_menu, menu, recent_menu: IUP_MENU
			sub_menu_file, sub_menu_edit, sub_menu_format, sub_menu_help, sub_menu_recent: IUP_SUBMENU
			btn_open, btn_save, btn_find: IUP_BUTTON
			lbl_statusbar, toolbar_sep: IUP_LABEL
			toolbar_hb: IUP_HBOX
		do
			gui := iup_open
			gui.load_images

			create cfg.config
			cfg.set_app_name("simple_notepad")
			x := cfg.load
			
			create ml.multiline
			ml.set_expand("YES")
			ml.set_cb_caret(agent multitext_caret_cb(?,?,?,?))

			i := cfg.get_variable_string("MainWindow", "Font")

			if not i.is_empty then
				ml.set_font(i)
			end

			create item_open.item("&Open...    %TCtrl+O")
			item_open.set_hide_mark(True)
			item_open.set_cb_action(agent item_open_action_cb(?))
			create btn_open.button("")
			btn_open.set_image("IUP_FileOpen")
			btn_open.set_flat(True)
			btn_open.set_can_focus(False)
			btn_open.set_tip("Open (Ctrl+O)")
			btn_open.set_cb_action(agent item_open_action_cb(?))

			create item_saveas.item("Save &As...%TCtrl+S")
			item_saveas.set_hide_mark(True)
			item_saveas.set_cb_action(agent item_saveas_action_cb(?))
			create btn_save.button("")
			btn_save.set_image("IUP_FileSave")
			btn_save.set_flat(True)
			btn_save.set_can_focus(False)
			btn_save.set_tip("Save (Ctrl+S)")
			btn_save.set_cb_action(agent item_saveas_action_cb(?))

			create item_exit.item("E&xit")
			item_exit.set_hide_mark(True)
			item_exit.set_cb_action(agent item_exit_action_cb(?))

			create item_find.item("&Find...%TCtrl+F")
			item_find.set_hide_mark(True)
			item_find.set_cb_action(agent item_find_action_cb(?))
			create btn_find.button("")
			btn_find.set_image("IUP_EditFind")
			btn_find.set_flat(True)
			btn_find.set_can_focus(False)
			btn_find.set_tip("Find (Ctrl+F)")
			btn_find.set_cb_action(agent item_find_action_cb(?))

			create item_goto.item("&Go To...%TCtrl+G")
			item_goto.set_hide_mark(True)
			item_goto.set_cb_action(agent item_goto_action_cb(?))

			create item_font.item("&Font...%TCtrl+F")
			item_font.set_hide_mark(True)
			item_font.set_cb_action(agent item_font_action_cb(?))

			create item_about.item("&About...")
			item_about.set_hide_mark(True)
			item_about.set_cb_action(agent item_about_action_cb(?))

			create item_copy.item("Copy%TCtrl+C")
			item_copy.set_name("ITEM_COPY")
			item_copy.set_hide_mark(True)
			item_copy.set_cb_action(agent item_copy_action_cb(?))
			create item_paste.item("Paste%TCtrl+V")
			item_paste.set_name("ITEM_PASTE")
			item_paste.set_hide_mark(True)
			item_paste.set_cb_action(agent item_paste_action_cb(?))
			create item_cut.item("Cut%TCtrl+X")
			item_cut.set_name("ITEM_CUT")
			item_cut.set_hide_mark(True)
			item_cut.set_cb_action(agent item_cut_action_cb(?))
			create item_delete.item("Delete%TDel")
			item_delete.set_name("ITEM_DELETE")
			item_delete.set_hide_mark(True)
			item_delete.set_cb_action(agent item_delete_action_cb(?))
			create item_select_all.item("Select All%TCtrl+A")
			item_select_all.set_hide_mark(True)
			item_select_all.set_cb_action(agent item_select_all_action_cb(?))

			create sep1.separator
			create sep2.separator
			create sep3.separator
			create toolbar_sep.label_empty
			toolbar_sep.set_vertical_separator
			create recent_menu.menu_empty
			create sub_menu_recent.submenu("Recent &Files", recent_menu)

			create file_menu.menu({ARRAY[IUP_MENU_ELEMENT] 1, << item_open, item_saveas, sep1, sub_menu_recent, item_exit >>})
			create edit_menu.menu({ARRAY[IUP_MENU_ELEMENT] 1, << item_cut, item_copy, item_paste, item_delete, sep2, item_find, item_goto, sep3, item_select_all >>})
			create format_menu.menu({ARRAY[IUP_MENU_ELEMENT] 1, << item_font >>})
			create help_menu.menu({ARRAY[IUP_MENU_ELEMENT] 1, << item_about >>})

			edit_menu.set_cb_open(agent edit_menu_open_cb(?))

			create sub_menu_file.submenu("&File", file_menu)
			create sub_menu_edit.submenu("&Edit", edit_menu)
			create sub_menu_format.submenu("F&ormat", format_menu)
			create sub_menu_help.submenu("&Help", help_menu)
			
			create menu.menu({ARRAY[IUP_MENU_ELEMENT] 1, << sub_menu_file, sub_menu_edit, sub_menu_format, sub_menu_help >>})

			create toolbar_hb.hbox({ARRAY[IUP_WIDGET] 1, << btn_open, btn_save, toolbar_sep, btn_find >>})
			toolbar_hb.set_margin(5, 5)
			toolbar_hb.set_gap(2)

			create lbl_statusbar.label("Lin 1, Col 1")
			lbl_statusbar.set_name("STATUSBAR")
			lbl_statusbar.set_expand("HORIZONTAL")
			lbl_statusbar.set_padding(10, 5)

			create v.vbox({ARRAY[IUP_WIDGET] 1, << toolbar_hb, ml, lbl_statusbar >>})
			
	      create w.dialog(v)
			w.set_menu_widget(menu)
			w.set_cb_k_any(agent key_pressed(?, ?))
			w.set_title("Simple Notepad")
			w.set_predefined_size("HALF", "HALF")

			-- parent for some pre-defined dialogs in callbak functions
			gui.set_global_parent_dialog_widget(w)
			
			w.set_user_size(0, 0)
			w.set_cb_close(agent close_dialog_cb(?))

			cfg.set_recent_file_menu(recent_menu, agent config_recent_cb(?), 10)
			cfg.show_dialog(w, "MainWindow")
			
         gui.main_loop
			gui.close
		end

end
